home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
011
/
packdir.aqm
/
packdir.asm
Wrap
Assembly Source File
|
1985-11-22
|
20KB
|
484 lines
PAGE
PAGE 72,132
TITLE Program PACKDIR
COMMENT " by Ted Mirecki
Reads a sub-directory as a file, packs entries into
adjacent locations, eliminating deleted entries, and
deallocates empty sub-directory clusters.
"
COM GROUP CODE,DATA
ASSUME CS:COM, DS:COM, ES:COM, SS:COM
;**********************************************************************
;
; STRUCTURES
;
;**********************************************************************
; Layout of a directory entry.
DIRENT STRUC
DNAME DB 8 DUP(?) ;NAME OF FILE
DEXT DB ?,?,? ;EXTENSION
ATTR DB ? ;ATTRIBUTE BYTE
DB 10 DUP(?) ;RESERVED FOR DOS
DTIME DW ? ;FILE UPDATE TIME
DDATE DW ? ;FILE UPDATE DATE
STARTCL DW ? ;STARTING CLUSTER
DSIZE DD ? ;FILE SIZE, DOUBLE WORD
DIRENT ENDS
; Layout of a normal FCB
NORMFCB STRUC
DRIVE DB ? ;DRIVE ID
FNAME DB 8 DUP(?) ;FILE NAME
FEXT DB ?,?,? ;FILENAME EXTENSION
CURBLOK DW ? ;CURRENT BLOCK
RECSIZE DW ? ;RECORD SIZE FOR EACH READ
FSIZELO DW ? ;LO-ORDER WORD OF FILE SIZE
FSIZEHI DW 0 ;HI ORDER WORD OF FILE SIZE
FDATE DW ? ;DATE OF LAST UPDATE
DB 10 DUP(?) ;RESERVED FOR DOS
RECINBLOK DB ? ;RECORD WITHIN BLOCK
RELREC DW ? ;DOUBLE-WORD RELATIVE RECORD NO.
DW ?
NORMFCB ENDS
;**********************************************************************
;
; DATA SEGMENT
;
;**********************************************************************
DATA SEGMENT BYTE 'COM'
; Extended FCB No. 1 will be built from command line parameters passed
; by DOS, and used to look up the sub-directory in its parent.
XFCB1 DB 0FFH,0,0,0,0,0 ;FCB EXTENSION HEADER
DB 10H ;ATTR BYTE FOR SUB-DIRS
FCB1 NORMFCB<> ;NORMAL FCB FOLLOWS EXTENSION
; Extended FCB No. 2 will be built by DOS as a result of lookup in
; parent directory, and used open & read sub-directory file.
XFCB2 DB 0FFH,0,0,0,0,0,10H
FCB2 NORMFCB<>
; Message strings
DISKERR$ DB 0DH,0AH,'Disk read error',0DH,0AH,'$'
BADFAT$ DB 0DH,0AH,'FAT error: cluster chain not terminated'
CRLF$ DB 0DH,0AH,'$'
COUNT$ DB ' had'
INREC$ DB 6 DUP(' ') ;SPACE FOR INPUT REC COUNT DIGITS
DB ' clusters, now has'
OUTREC$ DB 6 DUP(' ') ;SPACE FOR OUTPUT COUNT DIGITS
DB 0DH,0AH,'$'
ENDMSG$ DB 0DH,0AH
NDIR$ DB 6 DUP(' ') ;SPACE FOR DIRECTORY COUNT DIGITS
DB ' Sub-Directories processed',0DH,0AH
FREED$ DB 6 DUP(' ') ;SPACE FOR FREED COUNT DIGITS
DB ' clusters freed',0DH,0AH,'$'
; Miscellaneous data values
FCODE DB 11H ;FUNCTION CODE FOR FIND FIRST
FILECLUS DW 0 ;FILE SIZE FROM TRACING CLUSTER CHAIN
FATCOUNT DW ? ;NUMBER OF ENTRIES IN FAT
CLUSLEN DW ? ;BYTES PER CLUSTER
SAVSTART DW ? ;SAVE STARTING CLUSTER OF FILE
SECSPER DB ? ;SECTORS PER CLUSTER
RETRY DB 3 ;3 RETRIES IF READ ERROR
INREC DW 0 ;CURRENT INPUT RECORD NO.
OUTREC DW 0 ;CURRENT OUTPUT RECORD NO
NDIRS DW 0 ;COUNT OF DIRECTORIES PROCESSED
FREED DW 0 ;COUNT OF CLUSTERS FREED
; Open-ended buffer for holding File Allocation Table
FATWORD LABEL WORD ;PROCESS FAT BY WORDS
DATA ENDS
;**********************************************************************
;
; CODE SEGMENT
;
;**********************************************************************
CODE SEGMENT BYTE PUBLIC 'COM'
ORG 80H ;PARAMATER AREA IN PSP
SUBDATA LABEL BYTE ;USE AS BUFFER FOR 1 SUBDIR ENTRY
ORG 100H ;CODE ORIGIN FOR COM PROGRAMS
PACKDIR PROC FAR
;**********************************************************************
; MAINLINE
;**********************************************************************
; Each called routine returns carry flag clear if no errors,
; set if error. If error, then DX points to error message.
; Display the message, then exit to DOS.
CALL GETNAME ;LOOK FOR FILENAME ON COMMAND LINE
CALL FATREAD ;READ IN THE FAT
JC MSG
NEXTNAM: CALL OPEN ;LOOK UP FILENAME & OPEN IT
JC EOJ ;ALL DONE WHEN NO MORE MATCHING NAMES
CALL DIRSIZE ;GET FILE SIZE BY TRACING CLUSTERS
JC MSG
CALL PROCESS ;PROCESS THE CONTENTS OF THE SUB-DIR
CALL CLOSE ;CLOSE THE SUB-DIR FILE
JMP NEXTNAM
EOJ: MOV AX,NDIRS ;CONVERT DIR COUNT TO ASCII
MOV SI,OFFSET COM:NDIR$
CALL I2ASC
MOV AX,FREED ;CONVERT FREED COUNT
MOV SI,OFFSET COM:FREED$
CALL I2ASC
MOV DX,OFFSET COM:ENDMSG$
MSG: MOV AH,9 ;FUNCTION 9 = DISPLAY STRING
INT 21H ;CALL DOS TO DISPLAY MESSAGE
INT 20H ;RETURN TO DOS
PACKDIR ENDP
;**********************************************************************
; PROCEDURE GETNAME
;**********************************************************************
; Test if filename was specified on command line. If not, build one
; consisting of all wild characters. Move name to FCB1, set DTA.
GETNAME PROC NEAR
MOV SI,5CH ;POINT TO 1ST FCB IN PREFIX
CMP BYTE PTR [SI+1],' ' ;TEST FOR NAME FROM CMD LINE
JNE GET1 ;NAME IS THERE
MOV CX,11 ;ELSE INSERT 11 WILD CHARS
LEA DI,[SI+1] ;INTO NAME FLD OF FCB1
MOV AL,'?'
REP STOSB
GET1: MOV DI,OFFSET COM:FCB1 ;POINT TO FCB NO. 1
MOV CX,12 ;CX = LENGTH OF DRIVE, NAME & EXT
REP MOVSB ;MOVE NAME FROM PREFIX TO FCB 1
RET
GETNAME ENDP
;**********************************************************************
; PROCEDURE FATREAD
;**********************************************************************
; Get the FAT characteristics, calculate the size of the FAT, and read
; the FAT into the buffer. If read error, reset disk & retry 3 times.
FATREAD PROC NEAR
MOV DL,FCB2.DRIVE ;MOVE DRIVE ID INTO DL
PUSH DS ;SAVE THE DATA SEGMENT
MOV AH,1CH ;FUNCTION 1C = GET FAT INFO
INT 21H ;CALL DOS
POP DS ;RESTORE DATA SEGMENT
MOV SECSPER,AL ;SAVE SECTORS PER CLUSTER
MOV FATCOUNT,DX ;SAVE NUMBER OF FAT ENTRIES
CBW ;CONVERT SECS PER CLUS TO WORD
MUL CX ;AX=AX*CX, BYTES PER CLUSTER
MOV CLUSLEN,AX ;STORE IT FOR FUTURE USE
MOV AX,FATCOUNT ;RESTORE ENTRY COUNT IN AX
MOV DX,AX ;REPEAT IT IN DX
INC DX ;ADD 1 TO ROUND UP
SHR DX,1 ;ENTRY COUNT DIVIDED BY 2
ADD AX,DX ;SIZE IN BYTES = 1.5 * ENTRY COUNT
ADD AX,CX ;ROUND UP TO NEXT SECTOR
DEC AX ; BY ADDING SECTOR SIZE LESS 1
CWD ;CONVERT TO DBL WORD IN DX:AX
DIV CX ;AX / CX = FAT LENGTH IN SECTORS
MOV CX,AX ;MOVE SECTOR COUNT TO CX
MOV AH,19H ;DETERMINE DEFAULT DRIVE
INT 21H ;RETURNS DRIVE ID IN AL
MOV DX,1 ;BEGIN AT SECTOR 1
MOV BX,OFFSET COM:FATWORD ;POINT TO BUFFER FOR FAT
F1: PUSH AX ;SAVE THE REGS,
PUSH BX ; BECAUSE INT 25
PUSH CX ; DESTROYS THEM
PUSH DX
INT 25H ;ABSOLUTE DISK READ
POP DX ;RESTORE REGS EXCEPT AX
POP CX
POP BX
JNC F2 ;JUMP IF NO DISK ERRORS
POPF ;POP FLAGS SAVED BY INT 25
SUB AH,AH ;AH=0 IS DISK RESET FUNCTION
INT 13H ;DISK I/O INTERRUPT
POP AX ;RESTORE SECTOR COUNT
DEC RETRY ;DECREMENT RETRY COUNT
JNZ F1 ;TRY READING AGAIN IF NOT ZERO
MOV DX,OFFSET COM:DISKERR$ ;ERROR EXIT
STC
RET
F2: POPF ;READ OK: RESTORE STACK
POP AX
CLC
RET
FATREAD ENDP
;**********************************************************************
; PROCEDURE OPEN - find sub-dir name & open it as a data file
;**********************************************************************
; Input: AH is 11 to find first name, 12 to find subsequent.
; If found, but not a sub-directory, look for next matching entry,
; until a matching sub-directory is found or there are no more
; matching entries. If no match, exit with carry flag on,
; else open the file, save starting cluster pointer,
; display its name & turn carry flag off.
OPEN PROC NEAR
MOV DX,OFFSET COM:XFCB2 ;SET DTA AT EXTENDED FCB 2
MOV AH,1AH ;FUNCTION 1A = SET DTA
INT 21H ;CALL DOS TO SET DTA
MOV DX,OFFSET COM:XFCB1 ;POINT TO EXTENDED FCB NO. 1
OP1: MOV AH,FCODE ;GET CODE 11 OR 12
INT 21H ;CALL DOS TO FIND FIRST/NEXT
CMP AL,0FFH ;AL=FF IF NOT FOUND
JNE OP2 ;SKIP IF FOUND
STC ;SET CARRY FLAG IF NOT FOUND
RET
OP2: MOV FCODE,12H ;CODE 12 = FIND NEXT
CMP BYTE PTR FCB2+1.ATTR,10H ;IF FOUND, TEST IF A SUB-DIR
JNE OP1 ;IF NOT, GO FIND NEXT ENTRY
CMP BYTE PTR FCB2+1.DNAME,'.';IF SUB-DIR, TEST IF PERIOD
JE OP1 ;IF SO, GO FIND NEXT ENTRY
MOV AX,FCB2+1.STARTCL ;GET STARTING CLUSTER FROM FCB
MOV SAVSTART,AX ;SAVE IT FOR FUTURE USE
MOV DX,OFFSET COM:XFCB2 ;POINT TO EXTENDED FCB
MOV AH,0FH ;OPEN FILE FUNCTION
INT 21H
MOV SI,OFFSET COM:FCB2.FNAME ;POINT TO NAME IN OPEN FCB
MOV CX,11 ;DISPLAY 11 CHARS OF FILENAME
MOV AH,2 ;DISPLAY CHARACTER FUNCTION
OP3: MOV DL,[SI] ;GET FILENAME CHARACTER
INT 21H ;DISPLAY IT
INC SI ;POINT TO NEXT CHAR IN NAME
LOOP OP3
CLC ;INDICATE NO ERRORS
RET
OPEN ENDP
;**********************************************************************
; PROCEDURE DIRSIZE
;**********************************************************************
; Trace thru FAT, counting clusters in allocation chain.
; For each cluster, add bytes per cluster to file size.
; At exit, file size in bytes is stored in open FCB, file size
; in clusters in location FILECLUS.
; Register usage: DX:AX is dword accumulator for file size
; BX is last cluster number
; CX contains count of FAT entries
; SI points to next FAT entry
; DI contains bytes per cluster
DIRSIZE PROC NEAR
MOV BX,SAVSTART ;PUT STARTING CLUSTER IN BX
MOV DI,CLUSLEN ;BYTES PER CLUSTER IN DI
MOV CX,FATCOUNT ;LOOP COUNT IN CX
SUB AX,AX ;ZERO OUT FILE SIZE
MOV DX,AX ;AND CLUSTER COUNT
MOV FILECLUS,AX ;AND CLUSTER COUNT
D1: ADD AX,DI ;UPDATE FILE LENGTH
ADC DX,0 ;CARRY INTO HI WORD
INC FILECLUS ;UPDATE CLUSTER COUNT
MOV SI,BX ;CALC BX*1.5 IN SI
SHR BX,1
PUSHF ;SAVE THE CARRY FLAG
ADD SI,BX
MOV BX,FATWORD[SI] ;GET NEXT FAT ENTRY
POPF ;RESTORE CARRY FROM SHIFT
JNC D2 ;SKIP IF BX WAS EVEN
SHR BX,1 ;IF ODD, RIGHT-JUST
SHR BX,1 ; HI 12 BITS OF CLUSTER NO.
SHR BX,1
SHR BX,1
D2: AND BX,0FFFH ;ZERO OUT 4 HI BITS
CMP BX,0FF8H ;TEST FOR END OF CHAIN
JGE D3 ;END IF CLUSTER IS FF8 OR ABOVE
LOOP D1 ;LOOP IF NOT
MOV DX,OFFSET COM:BADFAT$ ;IF FAT END BUT NOT CHAIN END,
STC ; THEN ERROR
RET
D3: MOV FCB2.FSIZELO,AX ;PUT FILE LENGTH INTO FCB
MOV FCB2.FSIZEHI,DX
CLC ;CLEAR ERROR FLAG
RET
DIRSIZE ENDP
;**********************************************************************
; PROCEDURE PROCESS
;**********************************************************************
; Read the sub-directory as a data file, 1 entry at a time.
; Write out entries ehich are not deleted. At first unused entry,
; write a zero-filled entry and truncate the file to that length.
; Regs: SI points to I/O buffer.
PROCESS PROC NEAR
SUB AX,AX
MOV INREC,AX
MOV OUTREC,AX
MOV SI,OFFSET COM:SUBDATA;POINT TO I/O BUFFER
MOV DX,SI ;SET DTA TO BUFFER
MOV AH,1AH
INT 21H
MOV FCB2.RECSIZE,32 ;INSERT REC SIZE INTO FCB
MOV DX,OFFSET COM:XFCB2 ;POINT TO EXTENDED FCB
READ: MOV AX,INREC ;GET RECORD (ENTRY) NO. TO READ
MOV FCB2.RELREC,AX ;PUT IT INTO FCB'S RELATIVE REC FLD
INC INREC ;UPDATE RECORD NO. FOR NEXT READ
MOV AH,21H ;PERFORM DIRECT ACCESS READ
INT 21H
TEST AL,AL ;EOF IF AL NOT ZERO
JNZ EOF
CMP BYTE PTR SUBDATA,0E5H ;IS ENTRY DELETED?
JE READ ;YES: DO NOT WRITE, GO READ NEXT
WRITE: MOV AX,OUTREC ;GET RECORD NO. TO WRITE
MOV FCB2.RELREC,AX ;PUT IT INTO FCB
INC OUTREC ;UPDATE REC NO. FOR NEXT READ
MOV AH,22H ;PERFORM DIRECT ACCESS WRITE
INT 21H
CMP BYTE PTR SUBDATA,0 ;NEVER-USED ENTRY?
JNE READ ;IF NOT, READ NEXT, ELSE EOF
EOF: RET ;RETURN AT END OF DATA
PROCESS ENDP
;**********************************************************************
; PROCEDURE CLOSE: Close the sub-dir file, display counts
;**********************************************************************
CLOSE PROC NEAR
MOV AX,OUTREC ;GET COUNT OF ENTIRES WRITTEN
TEST AX,AX ;MAKE SURE SOME WERE WRITTEN
JZ CL1
MOV FCB2.RELREC,AX ;SET RECORD COUNT IN FCB
SUB CX,CX ;WRITE NOTHING, JUST SET SIZE
MOV DX,OFFSET COM:XFCB2 ;POINT TO EXTENDED FCB
MOV AH,28H ;PERFORM BLOCK WRITE, SET SIZE
INT 21H
CL1: MOV AX,FILECLUS ;GET INPUT CLUSTER COUNT
ADD FREED,AX ;UPDATE FREED CLUSTER COUNT
MOV SI,OFFSET COM:INREC$
CALL I2ASC ;CONVERT COUNT TO ASCII DIGITS
MOV AX,OUTREC ;GET OUTPUT RECORD COUNT
MOV BX,32 ;BYTES PER ENTRY
MUL BX ;DX:AX = OUTPUT BYTES
DIV CLUSLEN ;GET OUTPUT CLUSTERS, REMDR IN DX
TEST DX,DX ;TEST FOR REMAINDER
JZ CL2
INC AX ;IF REMDR, ROUND UP TO NEXT CLUSTER
CL2: SUB FREED,AX ;TOTAL FREED = FREED + INPUT - OUTPUT
MOV SI,OFFSET COM:OUTREC$
CALL I2ASC ;CONVERT OUTPUT COUNT TO DIGITS
MOV DX,OFFSET COM:COUNT$ ;DISPLAY COUNT MSG
MOV AH,9
INT 21H
INC NDIRS ;INCREMENT DIRECTORY COUNT
RET
CLOSE ENDP
CODE ENDS
COMMENT " **************************************************************
MODULE I2ASC CONVERTS 2-BYTE INTEGER INTO 6-BYTE NUMERIC ASCII STRING
INPUT: NUMBER TO BE CONVERTED IN AX
DS:SI POINTS TO STRING TO RECEIVE OUTPUT
OUTPUT: STRING AT DS:SI, RIGHT JUSTIFIED, BLANK PADDED
IF AX=0, STRING IS 1 ZERO IN RIGHTMOST POSITION
IF AX<0, LEADING MINUS IS FLOATED BEFORE 1ST DIGIT
DS:DI POINTS TO FIRST NON-BLANK IN STRING
ALL OTHER REGISTERS UNCHANGED
************************************************************************
"
CODE SEGMENT BYTE PUBLIC 'COM'
ASSUME CS:COM
I2ASC PROC NEAR
PUBLIC I2ASC
PUSH ES ;SAVE REGISTERS
PUSH DX
PUSH CX
PUSH BX
PUSH AX
MOV AL,' ' ;BLANK OUT STRING
MOV DX,DS
MOV ES,DX
MOV DI,SI
CLD
MOV CX,6
REP STOSB
MOV DI,SI
ADD DI,5 ;POINT AT STRING END
MOV BX,10 ;BASE TEN DIVISOR
POP AX ;GET BINARY NUMBER INTO AX
PUSH AX
STD ;MOVE BACKWARDS THRU STRING
DIVLOOP: CWD ;CONVERT TO DBL WORD IN DX,AX
IDIV BX ;QUOTIENT IN AX, REMAINDER IN DX
TEST DL,80H ;TEST IF REMAINDER NEGATIVE
JZ $+4 ;SKIP IF POS
NEG DL ;ELSE GET ABS OF REM
OR DX,30H ;INSERT ASCII ZONE
XCHG AX,DX ;EXCHANGE QUOT & REM
STOSB ;STORE ASCII CHAR IN STRING
MOV AX,DX ;RESTORE QUOTIENT
TEST AX,0FFFFH ;TEST IF MORE DEC DIGITS
JNZ DIVLOOP
POP BX ;RESTORE INTEGER INTO BX
TEST BX,8000H ;TEST IF NUMBER NEG
JZ I2EXIT
MOV AL,'-' ;INSERT MINUS SIGN IF NEG
STOSB
I2EXIT: INC DI ;POINT AT LAST NON-BLANK
MOV AX,BX ;RESTORE REGS
POP BX
POP CX
POP DX
POP ES
CLD
RET
I2ASC ENDP
CODE ENDS
END PACKDIR